package com.yn.xgame.gate;

import com.yn.xgame.IFuncServerLike;
import com.yn.xgame.IMessage;
import com.yn.xgame.LoadInfoManager;
import com.yn.xgame.ThreadServer;
import com.yn.xgame.message.C2GPbMessage;
import com.yn.xgame.message.Mq2ClientMessage;
import com.yn.xgame.pb.GateClientProto;
import com.yn.xgame.pb.MessageIdProto;
import com.yn.xgame.po.LoadInfo;
import com.yn.xgame.util.CtxUtils;
import io.netty.channel.ChannelHandlerContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * GateServer
 * Created by wangboo on 17/7/12.
 */
public class GateServer extends ThreadServer implements IFuncServerLike {

    private static final Logger logger = LoggerFactory.getLogger(GateServer.class);
    private static final GateClientProto.G2CMessage heartBeatResp =
            GateClientProto.G2CMessage.newBuilder()
                    .setMsgid(MessageIdProto.MessageId.G2C_HeartBeatResp)
                    .build();
    private static final GateClientProto.G2CMessage KICK =
            GateClientProto.G2CMessage.newBuilder()
                    .setMsgid(MessageIdProto.MessageId.G2C_Kick)
                    .build();

    LoadInfo loadInfo;
    LoadInfoManager loadInfoManager;

    public GateServer() {
        super("Gate" + GateApp.config.getId());
        loadInfoManager = new LoadInfoManager();
        loadInfo = new LoadInfo(GateApp.config);
        useTimerManager();
        // 答应状态
        getTimerManager().tickForever(10_000, 10_000, this::report);
        // 向zookeeper汇报状态
        getTimerManager().tickForever(10_000, 10_000, this::updateToZK);
    }

    @Override
    public boolean handleMessage(IMessage message) {
        if (super.handleMessage(message)) return true;
        if (message instanceof C2GPbMessage) {
            // 从客户端过来的消息
            return handleC2GMessage((C2GPbMessage) message);
        } else if (message instanceof Mq2ClientMessage) {
            // MQ 转发过来的消息，需要将消息转发到指定客户端
            Mq2ClientMessage mqMsg = (Mq2ClientMessage) message;
            long id = mqMsg.getId();
            ChannelHandlerContext ctx = GateApp.ctxManager.get(id);
            if (ctx == null) {
                logger.warn("ctx id: {} is lost, when sendInter: {}", id, mqMsg.getBin());
                return true;
            }
            ctx.writeAndFlush(mqMsg);
        }
        return false;
    }

    private boolean handleC2GMessage(C2GPbMessage msg) {
        switch (msg.getMsgId()) {
            case C2G_HeartBeat:
                // 处理心跳
                msg.send(heartBeatResp);
                break;
            default:
                // 转发到Logic
                ChannelHandlerContext ctx = msg.getCtx();
                String logicTopic = CtxUtils.getLogicTopic(ctx);
                if (logicTopic == null) {
                    if (loadInfoManager.getWorkingList().size() == 0) {
                        //TODO 没有Logic服可用，是否应该考虑直接拒绝客户端请求，或者踢掉线
                        logger.error("there is no logic server available");
                        ctx.writeAndFlush(KICK);
                        return true;
                    }
                    // 是否有指定的测试Logic
                    if (msg.getPb().hasTestLogicId()) {
                        int testGateId = msg.getPb().getTestLogicId();
                        logicTopic = loadInfoManager.getopicById(testGateId);
                    }
                    if (logicTopic == null) {
                        logicTopic = loadInfoManager.getWorkingList().get(0).topic;
                    }
                    CtxUtils.setLogicTopic(ctx, logicTopic);
                    logger.debug("new conn, sendInter to logic: {}", logicTopic);
                }
                long sid = CtxUtils.getId(ctx);
                // 转发到对应logicServer
                GateApp.mqClient.sendPbRedirect(logicTopic, sid, GateApp.config.getTopic(), msg.getPb());
        }
        return true;
    }

    private void report() {
        loadInfoManager.report();
    }

    private void updateToZK() {
        int cur = GateApp.ctxManager.size();
        if (loadInfo.cur != cur) {
            loadInfo.cur = cur;
            logger.debug("update loadInfo to zk: {}", loadInfo);
            GateApp.zkClient.createOrsetData_EPHEMERAL("/gate/" + loadInfo.id, loadInfo);
        }

    }
}
